package day17.springdemo1;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Xiao
 * @date 2021/8/12
 */
public class ClasspathxmlApplicationContext implements ApplicationContext{

    /**定义一个Map集合，用于存储类名和该类的对象*/
    private Map<String, Object> map = new HashMap<>();

    /**定义ClasspathxmlApplicationContext类的有参构造接收传递过来的配置文件的名称*/
    public ClasspathxmlApplicationContext(String fileName) throws DocumentException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        //解析配置文件，将配置文件中定义的全类名取出存储至list集合
        List<String> classNames = readFile(fileName);
        //通过反射，将通过全类名创建的对象（值），以及全类名对应的类名（键）存入map集合中
        map = createInstance(classNames);
    }

    /**创建对象：通过反射，将通过全类名创建的对象（值），以及全类名对应的类名（键）存入map集合中*/
    private Map<String, Object> createInstance(List<String> classNames) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //创建一个Map集合存储类名和对象
        Map<String, Object> map = new HashMap<>();
        for (String className : classNames) {
            //通过全类名获取字节码文件对象
            Class clazz = Class.forName(className);
            //获取构造方法
            Constructor constructor = clazz.getConstructor();
            //创建对象
            Object instance = constructor.newInstance();
            //类名作为键，对象作为值，
            //getSimpleName()：获取类名
            String simpleName = clazz.getSimpleName();
            map.put(simpleName, instance);
        }
        return map;
    }

    /**获取全类名：解析配置文件，将配置文件中定义的全类名取出存储至list集合*/
    private List<String> readFile(String fileName) throws DocumentException {
        //定义集合，用于存储解析出来的全类名
        List<String> list = new ArrayList<>();
        //使用DOM4J解析传递进来的配置文件
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(new File(fileName));
        Element rootElement = document.getRootElement();
        List<Element> beans = rootElement.elements("bean");
        //获取配置中的全类名
        for (Element bean : beans) {
            String className = bean.attribute("class").getValue();
            //将全类名存储到list集合
            list.add(className);
        }
        return list;
    }

    @Override
    /**传入一个类名，返回Object类对象*/
    public Object getBean(String name) {
        return map.get(name);
    }

    @Override
    /**传入一个类名，和该类的字节码对象，返回该类对象*/
    public <T> T getBean(String name, Class<T> type) {
        return (T) map.get(name);
    }
}
